﻿using System;
using System.Collections.Generic;
using System.Xml.Serialization;
using LIUS.Beta.FileSystem.PathFileSuffix;

namespace KopainConcept
{

    /// <summary>This class is the very Concept.  A concept consists of several Notes.
    /// When a Concept gets closer to the GUI it is, in Kopain2's view, called a KopainDocument.
    /// </summary>
    public partial class Concept
    {

#region	Private properites.

        /// <summary>This property represents the unique id of this Concept.
        /// </summary>
        private Guid _id;

        private List<Note> _noteList;

        private PathFilenameSuffix _pathFilenameSuffix;

#endregion	//	Private properites.

#region	Public properties.

        /// <summary>This property returns a list of Notes.
        /// </summary>
        public List<Note> NoteList
        {
            get
            {
                System.Diagnostics.Debug.Assert(null != _noteList, "I strongly doubt the caller believes a null value can be returned.  I doubt it should be.");
                return _noteList;
            }
        }

        /// <summary>This is the getter for the path/filename/suffix of the Concept.
        /// </summary>
        public PathFilenameSuffix PathFilenameSuffix
        {
            get
            {
                ///// <summary>This property returns the full path, filename and suffix; everything needed to find the Concept in a file system.
                ///// </summary>
                //public string FileAndPath
                //{
                //    get
                //    {
                //        return System.IO.Path.ChangeExtension(System.IO.Path.Combine(_path, _fileNameSansSuffix), _suffix);
                //    }
                //}

                ///// <summary>This propertyr returns the filename without its suffix.
                ///// </summary>
                //public string Filename { get { return _fileNameSansSuffix; } }

                ///// <summary>This property returns the path of the Concept.
                ///// </summary>
                //public string Path { get { return _path; } }
                return _pathFilenameSuffix;
            }
        }

#endregion	//	Public properties.

#region	Constructors.

        /// <summary>This default constructor should always be called by any other constructor.
        /// </summary>
        private Concept()
        {
            //_conceptDocument = new Schemas.ConceptRoot( Guid.NewGuid(), Common.ConceptFileFormatVersion);
        }

        /// <summary>This constructor takes all data to create an object representing an already existing Concept.
        /// </summary>
        /// <param name="pathFilenameSuffix"></param>
        private Concept(PathFilenameSuffix pathFilenameSuffix)
            : this()
        {
            _pathFilenameSuffix = pathFilenameSuffix;
            //_fileNameSansSuffix = System.IO.Path.GetFileNameWithoutExtension(pathFilenameSuffix);
            //_path = System.IO.Path.GetDirectoryName(pathFilenameSuffix) + System.IO.Path.DirectorySeparatorChar;   //"\\";
            //_suffix = System.IO.Path.GetExtension(pathFilenameSuffix).TrimStart(new char[] { '.' });
        }

        /// <summary>This constructor creates a new Concept.  The file must not exist from before.
        /// </summary>
        /// <param name="pathFilenameSuffix"></param>
        /// <returns></returns>
        public static Concept CreateNew(PathFilenameSuffix pathFilenameSuffix)
        {
            ////System.IO.File.CreateText(fileAndPath);

            //// Create the Concept and populate it with one document.
            //Concept newConcept = new Concept(fileAndPath);
            //newConcept._conceptDocument.Note = Schemas.NoteType.AllocateArray(1);    // new Kopain2Concept.Schemas.NoteType[1];
            //newConcept._conceptDocument.Note[0] = new KopainConcept.Schemas.NoteType( Guid.NewGuid(), "NewFile",  CreateNewFileName( newConcept._path), 0);

            //newConcept.Save();

            //return newConcept;

            Schemas.ConceptRoot conceptDocument = Schemas.ConceptRoot.CreateNew(            Guid.NewGuid(), Common.ConceptFileFormatVersion);
            SaveRootDocument(pathFilenameSuffix, conceptDocument);
            return Concept.Open(pathFilenameSuffix);
        }

        /// <summary>This constructor opens an already existing Concept.
        /// </summary>
        /// <param name="pathFilenameSuffix"></param>
        /// <returns></returns>
        public static Concept Open(PathFilenameSuffix pathFilenameSuffix)
        {
            //XmlDocument document = new XmlDocument();
            //document.Load(fileAndPath);
            //document.Schemas.Add(null, Constants.ConceptXSDFileNameWPath);
            //TODO:Validate like in Kopain(1).

            Schemas.ConceptRoot conceptDocument;

            try
            {

                //  Load and deserialize File for use.
                //
                XmlSerializer serz = new XmlSerializer(typeof(Schemas.ConceptRoot));
                using (System.IO.FileStream filestream = new System.IO.FileStream(pathFilenameSuffix.ToString(), System.IO.FileMode.Open))
                {
                    conceptDocument = (Schemas.ConceptRoot)serz.Deserialize(filestream);
                    filestream.Close();
                }

            //}
            //catch (XmlException exc)
            //{
            //    throw new Exception.CorruptSaveFile(exc);
            //}
            //catch (System.Xml.Schema.XmlSchemaValidationException exc)
            //{
            //    Debug.Fail("XmlSchemaValidationException.  Maybe it is the wrong version of the Savefile.  Check Validate method.");

            //    throw; //  Rethrow.
            }
            catch (System.IO.FileNotFoundException exc)
            {
                throw new Exception.FileWasNotFoundException(exc);
            }

            //Concept ret = new Concept(fileAndPath, internalXmlDocument);
            Concept ret = new Concept(pathFilenameSuffix);

            ret._id = new Guid(conceptDocument.id);

            if (null == conceptDocument.Note)
            {   // A new Concept does not have any Note stored.  But we want one in the GUI/Business logic so create one.  Then the file will be created by the first save.
                ret._noteList = new List<Note>();
                ret._noteList.Add(Concept.Note.CreateEmpty(ret.PathFilenameSuffix.Path));
            }
            else
            {
                foreach (Schemas.NoteType noteType in conceptDocument.Note)
                {
                    System.Diagnostics.Debug.Assert( null == ret._noteList, "When loading a Concept this property was null when I tested.");
                    ret._noteList = new List<Note>();
                    ret._noteList.Add(new Note(noteType.IDAsGuid, noteType.title, new FilenameSuffix( noteType.file, AllowPeriod.Yes, RequireSuffix.No ), noteType.cursorPosition));
                }
            }

            return ret;
        }

#endregion	//	Constructors.

#region	Public methods.

        /// <summary>This method finds the Document with the ID provided.
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public Note FindNoteByID(Guid id)
        {
            foreach (Note note in _noteList)
            {
                if (id == note.ID) { return note; }
            }
            System.Diagnostics.Debug.Fail("Should we really not find a guid?  Maybe throw an exception.");
            return null;
        }

        /// <summary>This method persists the Concept.
        /// </summary>
        public void Save()
        {
            // Create the Concept "Xml".
            Schemas.ConceptRoot document = Schemas.ConceptRoot.CreateNew(_id, Common.ConceptFileFormatVersion);

            //  Loop through the Notes and add them to the "Xml".  We save the Xml later.
            document.Note = Schemas.NoteType.AllocateArray(_noteList.Count);
            for (int i = 0; i < _noteList.Count; ++i)
            {
                //TODO:Update the cursor position.
                document.Note[i] = new Schemas.NoteType(_noteList[i].ID, _noteList[i].Title,  _noteList[i].FilenameSuffix, 0 /*Cursor position.*/);
            }

            // Loop through the Notes and save those files.
            foreach (Note note in _noteList)
            {
                if (note.SaveData(this._pathFilenameSuffix.Path))
                {
                    // If something should be updated in the document, this is the place.
                }
            }

            // Save the Concept XML.
            SaveRootDocument(this._pathFilenameSuffix, document);

        }

#endregion	//	Public methods.

#region	Private methods.

        ///// <summary>This method saves a Note (creates a file in the path).
        ///// </summary>
        ///// <param name="note"></param>
        //private void SaveNote(Note note)
        //{
        //    string pathAndFile = System.IO.Path.Combine(this._path, note.FileNameAndSuffix);
        //    using (System.IO.StreamWriter sw = System.IO.File.CreateText(pathAndFile))
        //    {
        //        //TODO:Update the text to whatever was in the textbox in the GUI.  A null value means we don't know anything and leave be.  I am not sure if an empty string should erase the file or leave it empty.  I suggest leave it until I have time setting a setting in the gui.
        //        sw.Write(note.Data);
        //        sw.Close();
        //    }
        //}

        /// <summary>This method saves the Root document (serializes and saves the xml document).  It does not save any siblings or children.
        /// </summary>
        /// <param name="pathFilenameSuffix"></param>
        /// <param name="conceptDocument"></param>
        private static void SaveRootDocument(PathFilenameSuffix pathFilenameSuffix, Schemas.ConceptRoot conceptDocument)
        {
            XmlSerializer serz = new XmlSerializer(typeof(Schemas.ConceptRoot));
            using (System.IO.StreamWriter writer = new System.IO.StreamWriter(pathFilenameSuffix.ToString()))
            {
                serz.Serialize(writer, conceptDocument);
                writer.Close();
            }
        }

        /// <summary>This helper method returns a new filename that doesn't exist in the folder (path).
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        private static string CreateNewFileName(string path)
        {
            System.Diagnostics.Debug.Assert(path.EndsWith(System.IO.Path.DirectorySeparatorChar.ToString()));

            //TODO:Write code to find a unique filename.
            return "NewConcept.kopain.txt";
        }

#endregion	//	Private methods.

    }

}
